package com.blue.base.common.utils.date;

import com.blue.base.common.enums.DateTimeEnum;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.*;

/**
 * 日期工具类
 *
 * @author liulei
 * @version 1.0
 */
public class LocalDateUtils {

    private static Logger logger = LoggerFactory.getLogger(LocalDateUtils.class);

    public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DATE_FORMAT = "yyyy-MM-dd";
    public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HHmmss");
    public static final DateTimeFormatter YEAR_MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM");
    public static final DateTimeFormatter SHORT_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyMMdd");
    public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT);
    public static final DateTimeFormatter MILLISECOND_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
    public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT);
    public static final DateTimeFormatter LONG_DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss SSS");

    /**
     * Date类型转时间字符串
     */
    public static String dateConvertToString(Date date, String format) {
        LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(format);
        return dateTimeFormatter.format(localDateTime);
    }

    /**
     * Date类型转时间字符串
     */
    public static String dateConvertToString(Date date) {
        LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT);
        return dateTimeFormatter.format(localDateTime);
    }

    /**
     * localDateTime转Date
     */
    public static Date localDateTime2Date(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 时间字符串转Date
     */
    public static Date stringConvertToDate(String date, String format) throws ParseException {
        return new SimpleDateFormat(format).parse(date);
    }

    /**
     * 获得当前日期
     *
     * @return
     */
    public static Date getNow() {
        Calendar cal = Calendar.getInstance();
        return cal.getTime();
    }

    /**
     * 获取一天的开始时间，2017,7,22 00:00
     */
    public static LocalDateTime getDayStart(LocalDateTime time) {
        return time.withHour(0).withMinute(0).withSecond(0).withNano(0);
    }

    /**
     * 获取某天的开始时间，2017,7,22 00:00
     */
    public static LocalDateTime getToDayStart(Date date) {
        return getDayStart(dateConvertToLocalDateTime(date));
    }

    /**
     * 获取一天的结束时间，2017,7,22 23:59:59.999999999
     */
    public static LocalDateTime getDayEnd(LocalDateTime time) {
        return time.withHour(23).withMinute(59).withSecond(59).withNano(999999999);
    }

    /**
     * 获取某天的结束时间，2017,7,22 23:59:59.999999999
     */
    public static LocalDateTime getTodayDayEnd(Date date) {
        return getDayEnd(dateConvertToLocalDateTime(date));
    }

    /**
     * 日期加上一个数,根据field不同加不同值,field为ChronoUnit.*
     */
    public static LocalDateTime plus(Date date, long number, TemporalUnit field) {
        LocalDateTime localDateTime = dateConvertToLocalDateTime(date);
        return localDateTime.plus(number, field);
    }

    /**
     * 日期加上一个数,根据field不同加不同值,field为ChronoUnit.*
     */
    public static LocalDateTime plus(LocalDateTime time, long number, TemporalUnit field) {
        return time.plus(number, field);
    }

    /**
     * 日期减去一个数,根据field不同减不同值,field参数为ChronoUnit.*
     */
    public static LocalDateTime minu(LocalDateTime time, long number, TemporalUnit field) {
        return time.minus(number, field);
    }

    /**
     * 获取两个日期的差 field参数为ChronoUnit.*
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @param field     单位(年月日时分秒)
     * @return 时间差值
     */
    public static long betweenTwoTime(LocalDateTime startTime, LocalDateTime endTime, ChronoUnit field) {
        Period period = Period.between(LocalDate.from(startTime), LocalDate.from(endTime));
        if (field == ChronoUnit.YEARS) {
            return period.getYears();
        }
        if (field == ChronoUnit.MONTHS) {
            return period.getYears() * 12 + period.getMonths();
        }
        return field.between(startTime, endTime);
    }

    /**
     * 返回当前的日期
     */
    public static LocalDate getCurrentLocalDate() {
        return LocalDate.now(ZoneOffset.of("+8"));
    }

    /**
     * 返回当前时间
     */
    public static LocalTime getCurrentLocalTime() {
        return LocalTime.now(ZoneOffset.of("+8"));
    }

    /**
     * 返回当前日期时间
     */
    public static LocalDateTime getCurrentLocalDateTime() {
        return LocalDateTime.now(ZoneOffset.of("+8"));
    }

    /**
     * yyyy-MM-dd
     */
    public static String getCurrentDateStr() {
        return LocalDate.now(ZoneOffset.of("+8")).format(DATE_FORMATTER);
    }

    /**
     * yyMMdd
     */
    public static String getCurrentShortDateStr() {
        return LocalDate.now(ZoneOffset.of("+8")).format(SHORT_DATE_FORMATTER);
    }

    public static String getCurrentMonthStr() {
        return LocalDate.now(ZoneOffset.of("+8")).format(YEAR_MONTH_FORMATTER);
    }

    /**
     * yyyy-MM-dd HH:mm:ss
     */
    public static String getCurrentDateTimeStr() {
        return LocalDateTime.now(ZoneOffset.of("+8")).format(DATETIME_FORMATTER);
    }

    public static String getCurrentLongDateTimeStr() {
        return LocalDateTime.now(ZoneOffset.of("+8")).format(LONG_DATETIME_FORMATTER);
    }

    /**
     * yyMMddHHmmss
     */
    public static String getCurrentShortDateTimeStr() {
        return LocalDateTime.now(ZoneOffset.of("+8")).format(MILLISECOND_FORMATTER);
    }

    /**
     * HHmmss
     */
    public static String getCurrentTimeStr() {
        return LocalTime.now(ZoneOffset.of("+8")).format(TIME_FORMATTER);
    }

    public static String getCurrentDateStr(String pattern) {
        return LocalDate.now(ZoneOffset.of("+8")).format(DateTimeFormatter.ofPattern(pattern));
    }

    public static String getCurrentDateTimeStr(String pattern) {
        return LocalDateTime.now(ZoneOffset.of("+8")).format(DateTimeFormatter.ofPattern(pattern));
    }

    public static String getCurrentTimeStr(String pattern) {
        return LocalTime.now(ZoneOffset.of("+8")).format(DateTimeFormatter.ofPattern(pattern));
    }

    public static LocalDate parseLocalDate(String dateStr, String pattern) {
        return LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(pattern));
    }

    public static LocalDateTime parseLocalDateTime(String dateTimeStr, String pattern) {
        return LocalDateTime.parse(dateTimeStr, DateTimeFormatter.ofPattern(pattern));
    }

    public static LocalTime parseLocalTime(String timeStr, String pattern) {
        return LocalTime.parse(timeStr, DateTimeFormatter.ofPattern(pattern));
    }

    public static String formatLocalDate(LocalDate date, String pattern) {
        return date.format(DateTimeFormatter.ofPattern(pattern));
    }

    public static String formatLocalDateTime(LocalDateTime datetime, String pattern) {
        return datetime.format(DateTimeFormatter.ofPattern(pattern));
    }

    public static String formatLocalTime(LocalTime time, String pattern) {
        return time.format(DateTimeFormatter.ofPattern(pattern));
    }

    public static LocalDate parseLocalDate(String dateStr) {
        return LocalDate.parse(dateStr, DATE_FORMATTER);
    }

    public static LocalDateTime parseLocalDateTime(String dateTimeStr) {
        return LocalDateTime.parse(dateTimeStr, DATETIME_FORMATTER);
    }

    public static LocalDateTime parseLongLocalDateTime(String longDateTimeStr) {
        return LocalDateTime.parse(longDateTimeStr, LONG_DATETIME_FORMATTER);
    }

    public static LocalTime parseLocalTime(String timeStr) {
        return LocalTime.parse(timeStr, TIME_FORMATTER);
    }

    public static String formatLocalDate(LocalDate date) {
        return date.format(DATE_FORMATTER);
    }

    public static String formatLocalDateTime(LocalDateTime datetime) {
        return datetime.format(DATETIME_FORMATTER);
    }

    public static String formatLocalTime(LocalTime time) {
        return time.format(TIME_FORMATTER);
    }

    /**
     * 日期相隔秒
     */
    public static long periodHours(LocalDateTime startDateTime, LocalDateTime endDateTime) {
        return Duration.between(startDateTime, endDateTime).get(ChronoUnit.SECONDS);
    }

    /**
     * 日期相隔天数
     */
    public static long periodDays(LocalDate startDate, LocalDate endDate) {
        return startDate.until(endDate, ChronoUnit.DAYS);
    }

    /**
     * 日期相隔周数
     */
    public static long periodWeeks(LocalDate startDate, LocalDate endDate) {
        return startDate.until(endDate, ChronoUnit.WEEKS);
    }

    /**
     * 日期相隔月数
     */
    public static long periodMonths(LocalDate startDate, LocalDate endDate) {
        return startDate.until(endDate, ChronoUnit.MONTHS);
    }

    /**
     * 日期相隔年数
     */
    public static long periodYears(LocalDate startDate, LocalDate endDate) {
        return startDate.until(endDate, ChronoUnit.YEARS);
    }

    /**
     * 是否当天
     */
    public static boolean isToday(LocalDate date) {
        return getCurrentLocalDate().equals(date);
    }

    /**
     * 获取当前毫秒数
     */
    public static Long toEpochMilli(LocalDateTime dateTime) {
        return dateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * 判断是否为闰年
     *
     * @param localDate 日期
     * @return boolean
     */
    public static boolean isLeapYear(LocalDate localDate) {
        return localDate.isLeapYear();
    }

    /**
     * 将java.com.blue.base.web.util.Date 转换为java8 的java.time.LocalDateTime,默认时区为东8区
     *
     * @param date 日期
     * @return localDateTime
     */
    public static LocalDateTime dateConvertToLocalDateTime(Date date) {
        return date.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime();
    }

    /**
     * 将java8 的 java.time.LocalDateTime 转换为 java.com.blue.base.web.util.Date，默认时区为东8区
     */
    public static LocalDate dateConvertToLocalDate(Date date) {
        return date.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDate();
    }

    /**
     * 毫秒转LocalDateTime
     */
    public static LocalDateTime millisecToDatetime(long milliseconds) {
        Instant instant = Instant.ofEpochMilli(milliseconds);
        return LocalDateTime.ofInstant(instant, ZoneOffset.of("+8"));
    }

    /**
     * date转换为localDate
     *
     * @param date 日期
     */
    public static LocalDate date2LocalDate(Date date) {
        return date.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDate();
    }

    /**
     * 将LocalDataTime转为毫秒数
     */
    public static long datatimeToTimestamp(LocalDateTime ldt) {
        return ldt.toInstant(ZoneOffset.of("+8")).toEpochMilli();
    }

    /**
     * 把long 转换成 日期 再转换成Date类型
     */
    public static Date transferLongToDate(Long millSec) {
        return new Date(millSec);
    }

    /**
     * 获取几天前的时间
     *
     * @param day x天
     * @return 日期
     */
    public static Date getDateBefore(int day) {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, -day);
        return calendar.getTime();
    }

    /**
     * 获取几天前的日期字符串
     *
     * @param day x天
     */
    public static String getDateBeforeByLocalDateTime(int day) {
        return dateConvertToLocalDateTime(getDateBefore(day)).format(DATETIME_FORMATTER);
    }

    /**
     * 获取几天前的日期字符串，带格式化
     *
     * @param day    x天
     * @param format 格式
     */
    public static String getDateBeforeBySimpleDateFormat(int day, String format) {
        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        calendar.add(Calendar.DATE, -day);
        return sdf.format(calendar.getTime());
    }

    /**
     * 获取几天前的日期字符串
     *
     * @param day    x天
     * @param format 格式
     */
    public static String getDateAfterBySimpleDateFormat(int day, String format) {
        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        calendar.add(Calendar.DATE, day);
        return sdf.format(calendar.getTime());
    }

    /**
     * 时间字符串转localDate
     *
     * @param date   日期字符串
     * @param format 格式
     */
    public static LocalDate stringConvertToLocalDate(String date, String format) throws ParseException {
        return dateConvertToLocalDate(new SimpleDateFormat(format).parse(date));
    }

    /**
     * 获取两个日期之间的所有日期
     *
     * @param start 开始日期
     * @param end   结束日期
     * @return 日期集合
     */
    public static List<LocalDate> getAscDateList(String start, String end) throws ParseException {
        LocalDate startDate = stringConvertToLocalDate(start, DATE_TIME_FORMAT);
        LocalDate endDate = stringConvertToLocalDate(end, DATE_TIME_FORMAT);
        List<LocalDate> result = new ArrayList<>();
        if (endDate.compareTo(startDate) <= 0) {
            return result;
        }
        while (true) {
            result.add(startDate);
            if (startDate.compareTo(endDate) >= 0) {
                break;
            }
            startDate = startDate.plusDays(1);
        }
        return result;
    }

    /**
     * 获取最近的十二个月
     */
    public static List<String> getLast12Months() {
        ArrayList<String> latest12Months = new ArrayList<>(12);
        LocalDate today = LocalDate.now();
        for (long i = 11L; i >= 0L; i--) {
            LocalDate localDate = today.minusMonths(i);
            String ss = localDate.toString().substring(0, 7);
            latest12Months.add(ss);
        }
        return latest12Months;
    }

    /**
     * 获取X年的十二个月份
     * @param localDate X年
     * @return X年的12个月
     */
    public static List<String> getYearOfMonths(LocalDate localDate) {
        List<String> months = Lists.newArrayListWithCapacity(12);
        int year = localDate.getYear();
        for (long i = 1; i <= 12; i++) {
            months.add(year + String.format("-%02d", i));
        }
        return months;
    }

    /**
     * 描述开始-结束的时间差
     *
     * @param start 开始
     * @param end   结束
     * @param unit  精确到单位
     * @return 描述
     */
    public static String getLocalDatePeriodDetail(LocalDateTime start, LocalDateTime end, TemporalUnit unit) {
        Duration duration = Duration.between(start, end);
        long days = duration.toDays();
        long hours = Duration.between(plus(start, days, ChronoUnit.DAYS), end).toHours();
        long minutes = Duration.between(plus(plus(start, hours, ChronoUnit.HOURS), days, ChronoUnit.DAYS), end).toMinutes();
        long seconds = Duration.between(plus(plus(plus(start, minutes, ChronoUnit.MINUTES), hours, ChronoUnit.HOURS), days, ChronoUnit.DAYS), end).getSeconds();
        logger.info("设备超期：{}", String.format("%d天%d时%d分%d秒", days, hours, minutes, seconds));
        if (unit instanceof ChronoUnit) {
            ChronoUnit f = (ChronoUnit) unit;
            switch (f) {
                case DAYS:
                    if (days > 0) {
                        return String.format("%d天", days);
                    }
                    if (hours > 0) {
                        return String.format("%d小时", hours);
                    }
                    if (minutes > 0) {
                        return String.format("%d分钟", minutes);
                    }
                    return String.format("%d秒", seconds);
                case HOURS:
                    return String.format("%d天%d小时", days, hours);
                case MINUTES:
                    return String.format("%d天%d小时%d分钟", days, hours, minutes);
                case SECONDS:
                    return String.format("%d天%d小时%d分钟%d秒", days, hours, minutes, seconds);
                default:
                    return String.format("%d天%d小时%d分钟%d秒", days, hours, minutes, seconds);
            }
        }
        return String.format("%d天%d时%d分%d秒", days, hours, minutes, seconds);
    }

    public static void main(String[] args) throws ParseException {
        System.out.println(getYearOfMonths(LocalDate.now()));
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATE_TIME_FORMAT);
        LocalDateTime biggerDate = dateConvertToLocalDateTime(simpleDateFormat.parse("2020-02-20 00:00:00"));
        logger.info(getLocalDatePeriodDetail(LocalDateTime.now(), biggerDate, ChronoUnit.DAYS));
        logger.info(getLocalDatePeriodDetail(LocalDateTime.now(), biggerDate, ChronoUnit.HOURS));
        logger.info(getLocalDatePeriodDetail(LocalDateTime.now(), biggerDate, ChronoUnit.MINUTES));
        logger.info(getLocalDatePeriodDetail(LocalDateTime.now(), biggerDate, ChronoUnit.SECONDS));
        logger.info(getLocalDatePeriodDetail(LocalDateTime.now(), biggerDate, ChronoUnit.NANOS));

        logger.info("{}", stringConvertToDate("2018-06-01 10:12:05", DATE_FORMAT));
        logger.info("23天前的日期{}", getDateBeforeByLocalDateTime(23).substring(0, 10));
        logger.info(millisecToDatetime(1563867467000L).format(DATETIME_FORMATTER));
        logger.info("格式化时间为数字{}", datatimeToTimestamp(getCurrentLocalDateTime()));
        logger.info(getCurrentDateTimeStr("yyyy年MM月dd日 HH:mm:ss"));
        logger.info("当前时间{}", getCurrentDateTimeStr());

        Date date = getNow();
        LocalDateTime localDateTime = dateConvertToLocalDateTime(date);
        Long localDateTimeSecond = localDateTime.toEpochSecond(ZoneOffset.of("+8"));
        Long dateSecond = date.toInstant().atOffset(ZoneOffset.of("+8")).toEpochSecond();
        logger.info("dateSecond:{}", dateSecond);
        logger.info("localDateTimeSecond:{}", localDateTimeSecond);

        // 增加二十分钟
        logger.info(formatLocalDateTime(plus(LocalDateTime.now(), 20, ChronoUnit.MINUTES), "yyyy年MM月dd日 HH:mm"));
        // 增加两年
        logger.info(formatLocalDateTime(plus(LocalDateTime.now(), 2, ChronoUnit.YEARS), "yyyy年MM月dd日 HH:mm"));

        LocalDateTime start = LocalDateTime.of(1993, 10, 13, 11, 11);
        LocalDateTime end = LocalDateTime.of(1994, 11, 13, 13, 13);
        logger.info("年:{}", betweenTwoTime(start, end, ChronoUnit.YEARS));
        logger.info("月:{}", betweenTwoTime(start, end, ChronoUnit.MONTHS));
        logger.info("日:{}", betweenTwoTime(start, end, ChronoUnit.DAYS));
        logger.info("半日:{}", betweenTwoTime(start, end, ChronoUnit.HALF_DAYS));
        logger.info("小时:{}", betweenTwoTime(start, end, ChronoUnit.HOURS));
        logger.info("分钟:{}", betweenTwoTime(start, end, ChronoUnit.MINUTES));
        logger.info("秒:{}", betweenTwoTime(start, end, ChronoUnit.SECONDS));
        logger.info("毫秒:{}", betweenTwoTime(start, end, ChronoUnit.MILLIS));


        String expireDate = dateConvertToString(transferLongToDate(1617055978822L), DATE_TIME_FORMAT);
        String currentDate = dateConvertToString(transferLongToDate(1617011958736L), DATE_TIME_FORMAT);
        logger.info("token过期时间:{}", expireDate);
        logger.info("当前时间:{}", currentDate);
    }
}
